/***********************************************************************
 *
 *		ASSEMBLE.CPP
 *		Assembly Routines for 68000 Assembler
 *
 *    Function: processFile()
 *		Assembles the input file. For each pass, the function
 *		passes each line of the input file to assemble() to be
 *		assembled. The routine also makes sure that errors are
 *		printed on the screen and listed in the listing file
 *		and keeps track of the error counts and the line
 *		number.
 *
 *		assemble()
 *		Assembles one line of assembly code. The line argument
 *		points to the line to be assembled, and the errorPtr
 *		argument is used to return an error code via the
 *		standard mechanism. The routine first determines if the
 *		line contains a label and saves the label for later
 *		use. It then calls instLookup() to look up the
 *		instruction (or directive) in the instruction table. If
 *		this search is successful and the parseFlag for that
 *		instruction is TRUE, it defines the label and parses
 *		the source and destination operands of the instruction
 *		(if appropriate) and searches the flavor list for the
 *		instruction, calling the proper routine if a match is
 *		found. If parseFlag is FALSE, it passes pointers to the
 *		label and operands to the specified routine for
 *		processing.
 *
 *	 Usage: processFile()
 *
 *		assemble(line, errorPtr)
 *		char *line;
 *		int *errorPtr;
 *
 *      Author: Paul McKee
 *		ECE492    North Carolina State University
 *
 *        Date:	12/13/86
 *
 *    Modified: Chuck Kelly
 *              Monroe County Community College
 *              http://www.monroeccc.edu/ckelly
 *
 ************************************************************************/

#include <vcl.h>
#include <stdio.h>
#include <ctype.h>
#include "asm.h"
#include "assembleS.h"
#include "mainS.h"
#include "textS.h"
#include "editorOptions.h"



extern int loc;		// The assembler's location counter
extern int sectionLoc[16];     // section locations
extern int  sectI;              // current section
extern int offsetMode;         // True when processing Offset directive
extern bool showEqual;          // true to display equal after address in listing
extern char pass;		// pass counter
extern bool pass2;		// Flag set during second pass
extern bool endFlag;		// Flag set when the END directive is encountered
extern bool continuation;	// TRUE if the listing line is a continuation
extern char empty[];            // used in conditional assembly

extern int lineNum;
extern int lineNumL68;
extern int errorCount, warningCount;

extern char line[256];		// Source line
extern FILE *inFile;		// Input file
extern FILE *listFile;		// Listing file
extern FILE *objFile;	        // Object file
extern FILE *errFile;		// error message file
extern FILE *tmpFile;           // temp file

extern int labelNum;            // macro label \@ number
extern bool listFlag;           // True if a listing is desired
extern bool objFlag;	        // True if an object code file is desired
extern bool xrefFlag;	        // True if a cross-reference is desired
extern bool CEXflag;	        // True is Constants are to be EXpanded
extern bool BITflag;            // True to assemble bitfield instructions
extern char lineIdent[];        // "mmm" used to identify macro in listing
//extern char arguments[MAX_ARGS][ARG_SIZE+1];    // macro arguments

extern bool CREflag, MEXflag, SEXflag;   // assembler directive flags
//extern bool endmFlag;           // ENDM opcode assembled
extern int macroNestLevel;      // count nested macro calls
extern char buffer[256];  //ck used to form messages for display in windows
extern char numBuf[20];
extern char globalLabel[SIGCHARS+1];
extern int includeNestLevel;    // count nested include directives
extern char includeFile[256];  // name of current include file
extern bool includedFileError; // true if include error message displayed

extern unsigned int stcLabelI;  // structured if label number
extern unsigned int stcLabelW;  // structured while label number
extern unsigned int stcLabelR;  // structured repeat label number
extern unsigned int stcLabelF;  // structured for label number
extern unsigned int stcLabelD;  // structured dbloop label number

bool skipList;                  // true to skip listing line
bool skipCond;                  // true conditionally skips lines
bool printCond;                 // true to print condition on listing line
bool skipCreateCode;            // true to skip calling createCode during macro processing

const int MAXT = 128;           // maximum number of tokens
const int MAX_SIZE = 512;       // maximun size of input line
char *token[MAXT];              // pointers to tokens
char tokens[MAX_SIZE];          // place tokens here
char *tokenEnd[MAXT];           // where tokens end in source line
int nestLevel = 0;              // nesting level of conditional directives

extern bool mapROM;             // memory map flags
extern bool mapRead;
extern bool mapProtected;
extern bool mapInvalid;

extern stack<int,vector<int> > stcStack;
extern stack<char, vector<char> > dbStack;
extern stack<String, vector<String> > forStack;

//------------------------------------------------------------
// Assemble source file
int assembleFile(char fileName[], char tempName[], AnsiString workName)
{
  AnsiString outName;
  int i;

  try {
    tmpFile = fopen(tempName, "w+");
    if (!tmpFile) {
      sprintf(buffer,"Error creating temp file.");
      Application->MessageBox(buffer, "Error", MB_OK);
      return SEVERE;
    }

    inFile = fopen(fileName, "r");
    if (!inFile) {
      Application->MessageBox("Error reading source file.", "Error", MB_OK);
      return SEVERE;
    }

    // if generate listing is checked then create .L68 file
    if (Options->chkGenList->Checked) {
      outName = ChangeFileExt(workName, ".L68");
      initList(outName.c_str());                // initialize list file
    }

    // if Object file flag then create .S68 file (S-Record)
    if (objFlag) {
      outName = ChangeFileExt(workName, ".S68");
      if(initObj(outName.c_str()) != NORMAL)     // if error initializing object file
        objFlag = false;                        // disable object file creation
    }

    // Assemble the file
    processFile();

    // Close files and print error and warning counts
    fclose(inFile);
    fclose(tmpFile);
    finishList();
    if (objFlag)
      finishObj();

    clearSymbols();               //ck clear symbol table memory

    // clear stacks used in structured assembly
    while(stcStack.empty() == false)
      stcStack.pop();
    while(dbStack.empty() == false)
      dbStack.pop();
    while(forStack.empty() == false)
      forStack.pop();

    // minimize message area if no errors or warnings
    if (warningCount == 0 && errorCount == 0) {
      TTextStuff *Active = (TTextStuff*)Main->ActiveMDIChild; //grab active mdi child
        Active->Messages->Height = 7;
    }

    AssemblerBox->lblStatus->Caption = IntToStr(warningCount);
    AssemblerBox->lblStatus2->Caption = IntToStr(errorCount);

    if(errorCount == 0 && errorCount == 0)
    {
      AssemblerBox->cmdExecute->Enabled = true;
    }
  }
  catch( ... ) {
    sprintf(buffer, "ERROR: An exception occurred in routine 'assembleFile'. \n");
    printError(NULL, EXCEPTION, 0);
    return NULL;
  }

  return NORMAL;
}


int strcap(char *d, char *s)
{
  bool capFlag;

  try {
	capFlag = true;
	while (*s) {
		if (capFlag)
			*d = toupper(*s);
		else
			*d = *s;
		if (*s == '\'')
			capFlag = !capFlag;
		d++;
		s++;
		}
	*d = '\0';
  }
  catch( ... ) {
    sprintf(buffer, "ERROR: An exception occurred in routine 'strcap'. \n");
    printError(NULL, EXCEPTION, 0);
    return NULL;
  }

  return NORMAL;
}


char *skipSpace(char *p)
{
  try {
	while (isspace(*p))
		p++;
	return p;
  }
  catch( ... ) {
    sprintf(buffer, "ERROR: An exception occurred in routine 'skipSpace'. \n");
    printError(NULL, EXCEPTION, 0);
    return NULL;
  }
}

// continue assembly process by reading source file and sending each
// line to assemble()
// does 2 passes from here
int processFile()
{
  int error;
  bool comment;                 // true when line is comment
  int value;
  bool backRef;

  try {
    offsetMode = false;         // clear flags
    showEqual = false;
    pass2 = false;
    macroNestLevel = 0;         // count nested macro calls
    includedFileError = false;  // true if include error message displayed
    mapROM = false;             // memory map flags
    mapRead = false;
    mapProtected = false;
    mapInvalid = false;

    for (pass = 0; pass < 2; pass++) {
      globalLabel[0] = '\0';    // for local labels
      labelNum = 0;             // macro label \@ number
      // evalNumber() contains error code that depends on the range of these numbers
      stcLabelI = 0x00000000;   // structured if label number
      stcLabelW = 0x10000000;   // structured while label number
      stcLabelF = 0x20000000;   // structured for label number
      stcLabelR = 0x30000000;   // structured repeat label number
      stcLabelD = 0x40000000;   // structured dbloop label number
      includeNestLevel = 0;     // count nested include directives
      includeFile[0] = '\0';    // name of current include file

      loc = 0;
      for (int i=0; i<16; i++)  // clear section locations
        sectionLoc[i] = 0;
      sectI = 0;                // current section

      lineNum = 1;
      lineNumL68 = 1;
      endFlag = false;
      errorCount = warningCount = 0;
      skipCond = false;             // true conditionally skips lines in code
      while(!endFlag && fgets(line, 256, inFile)) {
        error = OK;
        continuation = false;
        skipList = false;
        printCond = false;           // true to print condition on listing line
        skipCreateCode = false;

        assemble(line, &error);      // assemble one line of code

        lineNum++;
      }
      if (!pass2) {
        pass2 = true;
        //    ************************************************************
        //    ********************  STARTING PASS 2  *********************
        //    ************************************************************
      } else {                  // pass2 just completed
        if(!endFlag) {          // if no END directive was found
          error = END_MISSING;
          warningCount++;
          printError(listFile, error, lineNum);
        }
      }
      rewind(inFile);
    }
  }
  catch( ... ) {
    sprintf(buffer, "ERROR: An exception occurred in routine 'processFile'. \n");
    printError(NULL, EXCEPTION, 0);
    return NULL;
  }

  return NORMAL;
}

// Conditionally Assemble one line of code
int assemble(char *line, int *errorPtr)
{
  int value = 0;
  bool backRef = false;
  int error2Ptr = 0;
  char capLine[256];
  char *p;
  bool comment;                   // true when line is comment

  try {

    if (pass2 && listFlag)
      listLoc();

    strcap(capLine, line);
    p = skipSpace(capLine);               // skip leading white space
    tokenize(capLine, ", \t\n", token, tokens); // tokenize line
    if (*p == '*' || *p == ';')         // if comment
      comment = true;
    else
      comment = false;

    if (comment)                                // if comment
      if (pass2 && listFlag) {
        listLine(line, lineIdent);
        return NORMAL;
      }

    // conditional assembly for all code

    // ----- IFC -----
    if(!(stricmp(token[1], "IFC"))) {       // if IFC opcode
      if (token[0] != empty)                // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                        // nest level of skip
      else {
        if (stricmp(token[2], token[3])) {  // If IFC strings don't match
          skipCond = true;                  // conditionally skip lines
          nestLevel++;                      // nest level of skip
        }
        printCond = true;
      }

    // ----- IFNC -----
    } else if(!(stricmp(token[1], "IFNC"))) { // if IFNC opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[3] == empty) {                // if IFNC arguments missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          if (!(stricmp(token[2], token[3]))) { // if IFNC strings match
            skipCond = true;                    // conditionally skip lines
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- IFEQ -----
    } else if(!(stricmp(token[1], "IFEQ"))) { // if IFEQ opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[2] == empty) {                // if argument missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          eval(token[2], &value, &backRef, &error2Ptr);
          if (error2Ptr < ERRORN && value != 0) { // if not condition
            skipCond = true;                    // conditionally skip lines
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- IFNE -----
    } else if(!(stricmp(token[1], "IFNE"))) {  // if IFNE opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[2] == empty) {                // if argument missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          eval(token[2], &value, &backRef, &error2Ptr);
          if (error2Ptr < ERRORN && value == 0) { // if not condition
            skipCond = true;                    // skip lines in macro
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- IFLT -----
    } else if(!(stricmp(token[1], "IFLT"))) {  // if IFLT opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[2] == empty) {                // if argument missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          eval(token[2], &value, &backRef, &error2Ptr);
          if (error2Ptr < ERRORN && value >= 0) { // if not condition
            skipCond = true;                    // conditionally skip lines
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- IFLE -----
    } else if(!(stricmp(token[1], "IFLE"))) {  // if IFLE opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[2] == empty) {                // if argument missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          eval(token[2], &value, &backRef, &error2Ptr);
          if (error2Ptr < ERRORN && value > 0) { // if not condition
            skipCond = true;                    // conditionally skip lines
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- IFGT -----
    } else if(!(stricmp(token[1], "IFGT"))) {  // if IFGT opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[2] == empty) {                // if argument missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          eval(token[2], &value, &backRef, &error2Ptr);
          if (error2Ptr < ERRORN && value <= 0) { // if not condition
            skipCond = true;                    // conditionally skip lines
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- IFGE -----
    } else if(!(stricmp(token[1], "IFGE"))) {  // if IFGE opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (skipCond)
        nestLevel++;                          // nest level of skip
      else {
        if (token[2] == empty) {                // if argument missing
          NEWERROR(*errorPtr, INVALID_ARG);
        } else {
          eval(token[2], &value, &backRef, &error2Ptr);
          if (error2Ptr < ERRORN && value < 0) {  // if not condition
            skipCond = true;                    // conditionally skip lines
            nestLevel++;                        // nest level of skip
          }
        }
        printCond = true;
      }

    // ----- ENDC -----
    } else if(!(stricmp(token[1], "ENDC"))) {  // if ENDC opcode
      if (token[0] != empty)                  // if label present
        NEWERROR(*errorPtr, LABEL_ERROR);
      if (nestLevel > 0)
        nestLevel--;                          // decrease nesting level
      if (nestLevel == 0) {
        skipCond = false;                     // stop skipping lines
      } else
        printCond = false;

    } else if (!skipCond && !skipCreateCode) {  // else, if not skip condition and not skip create

      createCode(capLine, errorPtr);
    }

    // display and list errors and source line
    if (pass2) {
      if (*errorPtr > MINOR)
        errorCount++;
      else if (*errorPtr > WARNING)
        warningCount++;
      printError(listFile, *errorPtr, lineNum);
      if ( (listFlag && (!skipCond && !skipList)) || *errorPtr > WARNING)
      {
        if (printCond)
          listCond(skipCond);
        listLine(line, lineIdent);
      }
    }

  }
  catch( ... ) {
    NEWERROR(*errorPtr, EXCEPTION);
    sprintf(buffer, "ERROR: An exception occurred in routine 'assemble'. \n");
    return NULL;
  }

  return NORMAL;
}

// create machine code for instruction
int createCode(char *capLine, int *errorPtr) {
  instruction *tablePtr;
  flavor *flavorPtr;
  opDescriptor source, dest;
  char *p, *start, label[SIGCHARS+1], size, f;
  bool sourceParsed, destParsed;
  unsigned short mask, i;

  p = start = skipSpace(capLine);  // skip leading spaces and tabs
  if (*p && *p != '*' && *p != ';') {  // if line not empty and not comment
    // assume the line starts with a label
    i = 0;
    do {
      if (i < SIGCHARS)         // only first SIGCHARS of label are used
        label[i++] = *p;
      p++;
    } while (isalnum(*p) || *p == '.' || *p == '_' || *p == '$');
    label[i] = '\0';            // end label string with null
    if (i >= SIGCHARS)
      NEWERROR(*errorPtr, LABEL_TOO_LONG);

    // if next character is space AND the label was at the start of the line
    // OR the label ends with ':'
    if ((isspace(*p) && start == capLine) || *p == ':') {
      if (*p == ':')            // if label ends with :
        p++;                    // skip it
      p = skipSpace(p);         // skip trailing spaces
      if (*p == '*' || *p == ';' || !*p) {   // if the next char is '*' or ';' or end of line
        define(label, loc, pass2, true, errorPtr);  // add label to list of labels
        return NORMAL;
      }
    } else {
      p = start;                // reset p to start of line
      label[0] = '\0';          // clear label
    }
    p = instLookup(p, &tablePtr, &size, errorPtr);
    if (*errorPtr > SEVERE)
      return NORMAL;
    p = skipSpace(p);
    if (tablePtr->parseFlag) {
      // Move location counter to a word boundary and fix
      //   the listing before assembling an instruction
      if (loc & 1) {
        loc++;
        listLoc();
      }
      if (*label)
        define(label, loc, pass2, true, errorPtr);
      if (*errorPtr > SEVERE)
        return NORMAL;
      sourceParsed = destParsed = false;
      flavorPtr = tablePtr->flavorPtr;
      for (f = 0; (f < tablePtr->flavorCount); f++, flavorPtr++) {
        if (!sourceParsed && flavorPtr->source) {
          p = opParse(p, &source, errorPtr);    // parse source
          if (*errorPtr > SEVERE)
            return NORMAL;

          if (flavorPtr && flavorPtr->exec == bitField) {     // if bitField instruction
            p = skipSpace(p);           // skip spaces after source operand
            if (*p != ',') {            // if not Dn,addr{offset:width}
              p = fieldParse(p, &source, errorPtr);     // parse {offset:width}
              if (*errorPtr > SEVERE)
                return NORMAL;
            }
          }
          sourceParsed = true;
        }
        if (!destParsed && flavorPtr->dest) {   // if destination needs parsing
          p = skipSpace(p);     // skip spaces after source operand
          if (*p != ',') {
            NEWERROR(*errorPtr, COMMA_EXPECTED);
            return NORMAL;
          }
          p++;                   // skip over comma
          p = skipSpace(p);      // skip spaces before destination operand
          p = opParse(p, &dest, errorPtr);      // parse destination
          if (*errorPtr > SEVERE)
            return NORMAL;

          if (flavorPtr && flavorPtr->exec == bitField &&
              flavorPtr->source == DnDirect)  // if bitField instruction Dn,addr{offset:width}
          {
            p = skipSpace(p);           // skip spaces after destination operand
            if (*p != '{') {
              NEWERROR(*errorPtr, BAD_BITFIELD);
              return NORMAL;
            }
            p = fieldParse(p, &dest, errorPtr);
            if (*errorPtr > SEVERE)
              return NORMAL;
          }

          if (!isspace(*p) && *p) {     // if next character is not whitespace
            NEWERROR(*errorPtr, SYNTAX);
            return NORMAL;
          }
          destParsed = true;
        }
        if (!flavorPtr->source) {
          mask = pickMask( (int) size, flavorPtr, errorPtr);
          // The following line calls the function defined for the current
          // instruction as a flavor in instTable[]
          (*flavorPtr->exec)(mask, (int) size, &source, &dest, errorPtr);
          return NORMAL;
        }
        else if ((source.mode & flavorPtr->source) && !flavorPtr->dest) {
          if (*p!='{' && !isspace(*p) && *p) {
            NEWERROR(*errorPtr, SYNTAX);
            return NORMAL;
          }
          mask = pickMask( (int) size, flavorPtr, errorPtr);
          // The following line calls the function defined for the current
          // instruction as a flavor in instTable[]
          (*flavorPtr->exec)(mask, (int) size, &source, &dest, errorPtr);
          return NORMAL;
        }
        else if (source.mode & flavorPtr->source
                 && dest.mode & flavorPtr->dest) {
          mask = pickMask( (int) size, flavorPtr, errorPtr);
          // The following line calls the function defined for the current
          // instruction as a flavor in instTable[]
          (*flavorPtr->exec)(mask, (int) size, &source, &dest, errorPtr);
          return NORMAL;
        }
      }
      NEWERROR(*errorPtr, INV_ADDR_MODE);
    } else {
      // The following line calls the function defined for the current
      // instruction as a flavor in instTable[]
      (*tablePtr->exec)( (int) size, label, p, errorPtr);
      return NORMAL;
    }
  }
  return NORMAL;
}

//-------------------------------------------------------
// parse {offset:width}
char *fieldParse(char *p, opDescriptor *d, int *errorPtr)
{
  int offset, width;
  bool backRef;

  d->field = 0;

  if (*p != '{') {
    NEWERROR(*errorPtr, BAD_BITFIELD);
    return p;
  }
  p++;                          // skip '{'
  p = skipSpace(p);

  // parse offset
  if ((p[0] == 'D') && isRegNum(p[1])) {        // if offset in data register
    d->field |= 0x0800;         // set Do to 1 for Dn offset
    d->field |= ((p[1] - '0') << 6);    // put reg number in bits[8:6]
    p+=2;                       // skip Dn
  } else {                      // else offset is immediate
    if (p[0] == '#')
      p++;                      // skip '#'
    p = eval(p, &offset, &backRef, errorPtr);   // read offset number
    if (*errorPtr > SEVERE) {
      NEWERROR(*errorPtr, BAD_BITFIELD);
      return p;
    }
    if (!backRef)
      NEWERROR(*errorPtr, INV_FORWARD_REF);
    if (offset < 0 || offset > 31) {
      NEWERROR(*errorPtr, BAD_BITFIELD);
      return p;
    }
    d->field |= offset << 6;    // put offset in bits[10:6]
  }
  p = skipSpace(p);

  if (*p != ':') {
    NEWERROR(*errorPtr, BAD_BITFIELD);
    return p;
  }
  p++;          // skip ':'
  p = skipSpace(p);

  // parse width
  if ((p[0] == 'D') && isRegNum(p[1])) {        // if width in data register
    d->field |= 0x0020;         // set Dw to 1 for Dn width
    d->field |= (p[1] - '0');   // put reg number in bits[2:0]
    p+=2;                       // skip Dn
  } else {                      // else width is immediate
    if (p[0] == '#')
      p++;                      // skip '#'
    p = eval(p, &width, &backRef, errorPtr);   // read width number
    if (*errorPtr > SEVERE) {
      NEWERROR(*errorPtr, BAD_BITFIELD);
      return p;
    }
    if (!backRef)
      NEWERROR(*errorPtr, INV_FORWARD_REF);
    if (width < 1 || width > 32) {
      NEWERROR(*errorPtr, BAD_BITFIELD);
      return p;
    }
    if (width == 32)            // 0 specifies a field width of 32
      width = 0;
    d->field |= width;          // put width in bits[4:0]
  }
  if (*p != '}') {
    NEWERROR(*errorPtr, BAD_BITFIELD);
    return p;
  }
  p++;          // skip '}'
  return p;
}

//-------------------------------------------------------
int pickMask(int size, flavor *flavorPtr, int *errorPtr)
{
  if (!size || size & flavorPtr->sizes)
    if (size & (BYTE_SIZE | SHORT_SIZE))
      return flavorPtr->bytemask;
  else if (!size || size == WORD_SIZE)
    return flavorPtr->wordmask;
  else
    return flavorPtr->longmask;
  NEWERROR(*errorPtr, INV_SIZE_CODE);
  return flavorPtr->wordmask;
}

//---------------------------------------------------
// Tokenize a string to tokens.
// Each element of token[] is a pointer to the corresponding token in
//   tokens. token[0] is always reserved for the label if any. A value
//   of empty in token[] indicates no token.
// Each token is null terminated.
// Items inside parenthesis (  ) are one token
// Items inside single quotes ' ' are one token
// Parameters:
//      instr = the string to tokenize
//      delim = string of delimiter characters
//              (spaces are not default delimiters)
//              period delimiters are included in the start of the next token
//      token[] = pointers to tokens
//      tokens = new string full of tokens
// Returns number of tokens extracted.
int tokenize(char* instr, char* delim, char *token[], char* tokens){
  int i, size, tokN = 0, tokCount = 0;
  char* start;
  int parenCount;
  bool dotDelimiter;
  bool quoted = false;

  dotDelimiter = (strchr(delim, '.'));  // set true if . is a delimiter
  // clear token pointers
  for (i=0; i<MAXT; i++) {
    token[i] = empty;           // this makes the pointer point to empty
    tokenEnd[i] = NULL;         // clear positions
  }

  start = instr;
  while(*instr && isspace(*instr))              // skip leading spaces
    instr++;
  if (*instr != '*' && *instr != ';') {         // if not comment line
    if (start != instr)                         // if no label
      tokN = 1;
    size = 0;
    while (*instr && tokN < MAXT && size < MAX_SIZE) { // while tokens remain
      parenCount = 0;
      token[tokN] = &tokens[size];              // pointer to token
      //while (*instr && strchr(delim, *instr))   // skip leading delimiters
      while(*instr && isspace(*instr))              // skip leading spaces
        instr++;
      if (*instr == '\'' && *(instr+1) == '\'') { // if argument starts with '' (NULL)
        tokens[size++] = '\0';
        instr+=2;
      }
      if (dotDelimiter && *instr == '.') {      // if . delimiter
        tokens[size++] = *instr++;              // start token with .
      }
      // while more chars AND (not delimiter OR inside parens) AND token size limit not reached OR quoted
      while (*instr && (!(strchr(delim, *instr)) || parenCount > 0 || quoted) && (size < MAX_SIZE-1) ) {
        if (*instr == '\'')                     // if found '
          if (quoted)
            quoted = false;
          else
            quoted = true;
        if (*instr == '(')                      // if found (
          parenCount++;
        else if (*instr == ')')
          parenCount--;
        tokens[size++] = *instr++;
      }

      tokens[size++] = '\0';                    // terminate
      tokenEnd[tokN] = instr;                   // save token end position in source line
      if (*instr && (!dotDelimiter || *instr != '.')) // if not . delimiter
        instr++;                                // skip delimiter
      tokCount++;                               // count tokens
      tokN++;                                   // next token index
      //while (*instr && strchr(delim, *instr))       // skip trailing delimiters
      while (*instr && isspace(*instr))         // skip trailing spaces *ck 12-10-2005
        instr++;
    }
  }
  return tokCount;
}


